/**********************************************************
**   COMPILER.FRM
**   Coco/R C Support Frames.
**   Author: Frankie Arzu <farzu@uvg.edu.gt>
**
**   Nov 11, 2004   Version 1.5
**      C++ fixes: made class X
**      <alimoe>
**   Nov 11, 2004   Version 1.07
**      C++ fixes: Syms and enum
**      <alimoe>
**   Jun 12, 1996   Version 1.06
**      Many fixes and suggestions thanks to
**      Pat Terry <p.terry@ru.ac.za>
**   Oct 11, 1997   Version 1.07
**      Fix GetFileName to support relative and absolute 
**      paths for Unix
**   Mar 31, 1998   Version 1.08
**      Support for MS VC++ 5.0 
**   Oct 31, 1999   Version 1.14
**      Further compliance with latest gcc
**********************************************************/

#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

#if __MSDOS__ || MSDOS || WIN32 || __WIN32__
#  include  <io.h>
#else
#  include <unistd.h>
#  define  O_BINARY     0
#endif

#include "alvos.hpp"
#include "alvop.hpp"

struct ErrDesc {
	int nr, line, col;
	struct ErrDesc *next;
};

struct ErrDesc *FirstErr = NULL, *LastErr;
extern Error_Func Custom_Error;

static char *ErrorMsg[] = {
#include "alvoe.hpp"
	"User error number clash",
	""
};

class DefaultClassForThisUnit {
public:
	struct {
		char SourceName[256], ListName[256];
		FILE *lst;
		int Listinfo;
		int Errors;
	} CG;						//compiler_globals;

	void StoreError (int nr, int line, int col, long pos) {
	/* Store an error message for later printing */
		struct ErrDesc *NextErr;

		  NextErr = (struct ErrDesc *) malloc (sizeof (struct ErrDesc));
		  NextErr->nr = nr;
		  NextErr->line = line;
		  NextErr->col = col;
		  NextErr->next = NULL;
		if (FirstErr == NULL)
			FirstErr = NextErr;
		else
			  LastErr->next = NextErr;
		  LastErr = NextErr;
		  CG.Errors++;
	} 
	
	void SummarizeErrors (void) {
	/* Summarize error messages */
		struct ErrDesc *nextErr;
		nextErr = FirstErr;
		while (nextErr != NULL) {
			fprintf (CG.lst, "%s:%d:%d: ",
					 CG.SourceName, nextErr->line, nextErr->col);
			PrintErrMsg (nextErr->nr);
			fprintf (CG.lst, "\n");
			nextErr = nextErr->next;
		}
	}
	void SourceListing (void) {
	/* Generate the source listing */
		int i;
		strcpy (CG.ListName, CG.SourceName);

		i = strlen (CG.ListName) - 1;
		while (i > 0 && CG.ListName[i] != '.')
			i--;
		if (i > 0)
			CG.ListName[i] = '\0';


		strcat (CG.ListName, ".lst");
		if ((CG.lst = fopen (CG.ListName, "w")) == NULL) {
			fprintf (stderr, "Unable to open List file %s\n", CG.ListName);
			exit (EXIT_FAILURE);
		}
		PrintListing ();
		fclose (CG.lst);
	}

	void param_options (int argc, char *argv[]) {
		int i;
		char *s;

		for (i = 1; i < argc; i++) {
			s = argv[i];
#if __MSDOS__ || MSDOS
			if (*s == '-' || *s == '/') {
#else /* UNIX */
			if (*s == '-') {
#endif 
#if FOOL_SCITE
				}
#endif
				s++;
				while (*s) {
					switch (*s) {
					case 'l':
					case 'L':
						CG.Listinfo = 1;
						break;
					}
					s++;
				}
			} else
				strcpy (CG.SourceName, s);
		}
	}
	long GetLine (long pos, char *line){
	/* Read a source line.  Return empty line if eof */
		char ch;
		int i;
		int col = 1;

		ch = S_CurrentCh (pos);
		while (ch && ch != LF_CHAR && ch != EOF_CHAR) {
			if (ch == TAB_CHAR) {
				i = TAB_SIZE - (col % TAB_SIZE) + 1;
				while (i--) {
					*line++ = ' ';
					col++;
				}
			} else if (ch != CR_CHAR) {
				*line++ = ch;
				col++;
			}
			pos++;
			ch = S_CurrentCh (pos);
		}
		*line = 0;
		if (ch != EOF_CHAR)
			return pos + 1;
		else
			return -1;
	}

	void PrintErrMsg (int nr) {
	/* Modify this appropriately if semantic errors are to be handled by the */
	/* switch (nr) statement */
		if (nr <= MAXERROR)
			fprintf (CG.lst, "%s", ErrorMsg[nr]);
		else
			switch (nr) {
				/* add your customized error messages here */
			default:
				fprintf (CG.lst, "Compiler error");
			}
		fprintf (CG.lst, " (E%d)", nr);
	}

	void PrintErr (int nr, int col) {
		fprintf (CG.lst, "*****");
		while (col--)
			fprintf (CG.lst, " ");
		fprintf (CG.lst, " ^ ");
		PrintErrMsg (nr);
		fprintf (CG.lst, "\n");
	}

	void PrintListing (void){
	/* Print a source listing with error messages */
		struct ErrDesc *nextErr;
		int lnr, errC;
		long srcPos;
		char line[255];

		fprintf (CG.lst, "Listing:\n\n");
		srcPos = 0;
		nextErr = FirstErr;
		srcPos = GetLine (srcPos, line);
		lnr = 1;
		errC = 0;
		while (srcPos != -1) {
			fprintf (CG.lst, "%5d  %s\n", lnr, line);
			while (nextErr != NULL && nextErr->line == lnr) {
				PrintErr (nextErr->nr, nextErr->col);
				errC++;
				nextErr = nextErr->next;
			}
			srcPos = GetLine (srcPos, line);
			lnr++;
		}
		if (nextErr != NULL) {
			fprintf (CG.lst, "%5d\n", lnr);
			while (nextErr != NULL) {
				PrintErr (nextErr->nr, nextErr->col);
				errC++;
				nextErr = nextErr->next;
			}
		}
		fprintf (CG.lst, "\n\n%5d syntax errors\n", errC);
	}
	int rest (char *, void**);
	int xmain (int argc, char *argv[]) {
		/* check on correct parameter usage */
		strcpy (CG.SourceName, "");
		param_options (argc, argv);
		void *c;
		rest (CG.SourceName, &c);
	}
};

DefaultClassForThisUnit XX;

void SourceListing   (void) { XX.SourceListing  (); }
void SummarizeErrors (void) { XX.SummarizeErrors(); }
void StoreError      (int a, int b, int c, long d) { XX.StoreError     (a,b,c,d); }


class CFile1 { //RAII
public:
	CFile1(char*fn, int mode=O_RDONLY | O_BINARY) {
		printf("opening\n");
		handle_=open(fn,mode);
	}
	virtual ~CFile1() {
		printf("closing\n");
		if (!bad()) close(handle_);
		printf("closed\n");
	}
public:
	int handle() { return handle_; }
	bool bad() { return handle_ == -1; }
private:
	int handle_;
};

//#include "cocoC/AlvoP/alvopp.hh" // DEBUG11
#include "alvopp.hxx" // DEBUG11

char M[1024];
int DefaultClassForThisUnit::rest (char *sn, void**closure) {
	strcpy (M, "(no error)\n");

	{															// DEBUG11
		ALVO_C    ac1;											// DEBUG11
		ALVO_X    ax2;											// DEBUG11
																// DEBUG11
		ALVO     *t1 = ac1.int_(100, ax2.cont(*ac1.group()));	// DEBUG11
		*closure     = t1;										// DEBUG11
	}															// DEBUG11

	CG.lst = stderr;
	strncpy (CG.SourceName, sn, sizeof (CG.SourceName));

	CG.Listinfo = 0;
	CG.Errors = 0;

	if (!CG.SourceName[0]) {
		strcpy (M, "No input file specified\n");
		goto exit_failure;
	}

	{
		CFile1 sourceFile(CG.SourceName);
		/* open the source file (Scanner.S_src)  */
//		if ((S_src = open (CG.SourceName, O_RDONLY | O_BINARY)) == -1) {
		if (sourceFile.bad()) {
#ifdef __LINUX__
			snprintf (M, sizeof (M),
					  "Unable to open input file %s, errno (%d)\n",
					  CG.SourceName, errno);
#else
			sprintf (M, //sizeof (M),
					  "Unable to open input file %s, errno (%d)\n",
					  CG.SourceName, errno);
#endif
			goto exit_failure;
		} else {
			S_src = sourceFile.handle();
		}

		/* install error reporting procedure */
#ifdef __LINUX__
		extern void ::StoreError (void);
#endif
		Custom_Error = (Error_Func)::StoreError;

		/* parse the source */
		Parse ((void*)closure);
//		close (S_src);
	}

	if (CG.Errors)
		fprintf (stderr, "Compilation errors\n");

	if (CG.Listinfo)
		SourceListing ();
	else if (CG.Errors)
		SummarizeErrors ();

	return CG.Errors;
exit_failure:
	fprintf (stderr, "%s", M);
	exit (EXIT_FAILURE);
}

extern "C" int rest (char *sn, void **C) {
	int R=XX.rest (sn, C);
	return R;
}

#include "alvopp.hxx"

static ALVO default_alvo();

AlvoPP::AlvoPP() : /*stack(&s), */Result(&s), cur(C.list()) {}
int ev=0;
